/*
 * @Author: leox_tian
 * @Date: 2021-07-30 13:06:02
 * @Description: 最小二乘法拟合线条
 */

#pragma once

#include <Eigen/Core>
#include <Eigen/Dense>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>

/**参考链接：
 * http://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html
 * http://eigen.tuxfamily.org/dox/group__LeastSquares.html
 * https://blog.shipengx.com/archives/e0ebe48c.html
 *
 * 求解速度：正规方程LDLT>householderQr分解>矩阵常规法>bdcSvd分解
 * 求解精度：bdcSvd分解>householderQr分解>正规方程LDLT>矩阵常规法
 * LeastSquareFitBeeline()在拟合直线时最快，因为使用的是推导的公式
 */

class LineFit {
 private:
  Eigen::MatrixXf A_;
  Eigen::MatrixXf b_;

 public:
  LineFit();
  ~LineFit();
  Eigen::Vector2f LeastSquareFitBeeline(const std::vector<float>& X,
                             const std::vector<float>& Y);
  Eigen::Vector3f LeastSquareFitCurve(const std::vector<float>& X,
                                      const std::vector<float>& Y);
  void ConstructAb(const std::vector<float>& X, const std::vector<float>& Y,
                   uint8_t orders, Eigen::MatrixXf& A, Eigen::MatrixXf& b);

  Eigen::VectorXf LeastSquareNormalMethod(const std::vector<float>& X,
                                          const std::vector<float>& Y,
                                          uint8_t orders);
  Eigen::VectorXf LeastSquareSVDMethod(const std::vector<float>& X,
                                       const std::vector<float>& Y,
                                       uint8_t orders);
  Eigen::VectorXf LeastSquareQRMethod(const std::vector<float>& X,
                                      const std::vector<float>& Y,
                                      uint8_t orders);
  Eigen::VectorXf LeastSquareLDLTMethod(const std::vector<float>& X,
                                        const std::vector<float>& Y,
                                        uint8_t orders);
  Eigen::VectorXf SolveBySVD(const Eigen::MatrixXf& A,
                             const Eigen::MatrixXf& b);
  Eigen::VectorXf SolveByQR(const Eigen::MatrixXf& A, const Eigen::MatrixXf& b);
  Eigen::VectorXf SolveByLDLT(const Eigen::MatrixXf& A,
                              const Eigen::MatrixXf& b);
};
